package com.yuncheng.spcyApi.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import org.apache.commons.compress.archivers.zip.ParallelScatterZipCreator;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.parallel.InputStreamSupplier;
import org.apache.commons.io.input.NullInputStream;
import utils.Util;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.concurrent.*;
import java.util.zip.Deflater;

/**
 * @author css
 */
public class ZipCompressUtils {
    public static void main(String[] args) {
        final ThreadFactory factory = new ThreadFactoryBuilder().setNamePrefix("compressFileList-pool-").build();

//        List<String> paths = CollectionUtil.newArrayList("D:/test/sywj.pdf","D:/test/123.pdf");
        List<File> paths = CollectionUtil.newArrayList(new File("D:/test/sywj.pdf"),new File("D:/test/sywj.pdf"));
        List<String> mlList = CollectionUtil.newArrayList("ml1/1.pdf","ml2/2.pdf");

        try {
            ZipCompressUtils.compressFileList("D:/test/outZip.zip",0,paths,mlList);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        HttpServletResponse response = null;
        ZipCompressUtils.downloadZip("D:/test/outZip.zip",response);
    }

    /**
     * 压缩文件夹
     *
     * @param zipOutName zip输出路径
     * @param paths      将要压缩的路径
     * @throws IOException
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void compressFiles(String zipOutName, List<String> paths) throws IOException, ExecutionException, InterruptedException {
//        创建一个线程池对象
        ExecutorService executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
//        压缩等级默认为速度优先
        compressFiles(zipOutName, executor, Deflater.BEST_SPEED, paths);
    }

    public static void compressFileList(String zipOutName, List<File> paths, List<String> mlList) throws IOException, ExecutionException, InterruptedException {
//        创建一个线程池对象
        ExecutorService executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
//        压缩等级默认为速度优先
        compressFileList(zipOutName, executor, Deflater.BEST_SPEED, paths, mlList);
    }

    /**
     * 自定义线程池
     *
     * @param zipOutName
     * @param executorService 线程池实现对象
     * @param paths
     * @throws IOException
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void compressFiles(String zipOutName, ExecutorService executorService, int level, List<String> paths) throws IOException, ExecutionException, InterruptedException {
//      创建用于多线程压缩文件的对象
        ParallelScatterZipCreator parallelScatterZipCreator = new ParallelScatterZipCreator(executorService);
//        输出文件流
        OutputStream outputStream = new FileOutputStream(zipOutName);
//        输出Zip文件流
        ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(outputStream);
//        设置压缩等级
        zipArchiveOutputStream.setLevel(level);
//        设置压缩的字符编码
        zipArchiveOutputStream.setEncoding("UTF-8");
//        循环压缩各个路径的文件
        for (String path : paths) {
            File temp = new File(path);
            compress(parallelScatterZipCreator, temp, temp.getName());
        }
//        将数据写入zip输出流
        parallelScatterZipCreator.writeTo(zipArchiveOutputStream);
//        相关流的关闭
        zipArchiveOutputStream.close();
        outputStream.close();
    }

    /**
     * 自定义线程池
     * @param zipOutName 压缩包文件路径+名称
     * @param executorService 线程池
     * @param level 压缩等级 0最高
     * @param paths 文件路径列表
     * @param mlList 文件目录列表
     * @throws IOException
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void compressFileList(String zipOutName, ExecutorService executorService, int level, List<File> paths, List<String> mlList) throws IOException, ExecutionException, InterruptedException {
//      创建用于多线程压缩文件的对象
        ParallelScatterZipCreator parallelScatterZipCreator = new ParallelScatterZipCreator(executorService);
//        输出文件流
        OutputStream outputStream = new FileOutputStream(zipOutName);
//        输出Zip文件流
        ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(outputStream);
//        设置压缩等级
        zipArchiveOutputStream.setLevel(level);
//        设置压缩的字符编码
        zipArchiveOutputStream.setEncoding("UTF-8");
//        循环压缩各个路径的文件
        for (int i=0; i<paths.size(); i++) {
//            compress(parallelScatterZipCreator, paths.get(i), paths.get(i).getName());
            compress(parallelScatterZipCreator, paths.get(i), mlList.get(i));
        }
//        将数据写入zip输出流
        parallelScatterZipCreator.writeTo(zipArchiveOutputStream);
//        相关流的关闭
        zipArchiveOutputStream.close();
        outputStream.close();
    }

    /**
     * 自定义线程创建工厂
     *
     * @param zipOutName
     * @param level      压缩等级
     * @param paths
     * @throws IOException
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void compressFiles(String zipOutName, int level, List<String> paths) throws IOException, ExecutionException, InterruptedException {
        // 线程创建工厂
        final ThreadFactory factory = new ThreadFactoryBuilder().setNamePrefix("compressFileList-pool-").build();
        ExecutorService executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), factory, new ThreadPoolExecutor.CallerRunsPolicy());
        compressFiles(zipOutName, executor, level, paths);
    }

    /**
     * 自定义线程创建工厂
     * @param zipOutName
     * @param level 压缩等级 0 最高
     * @param paths 文件路径列表
     * @param mlList 文件目录列表
     * @throws IOException
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void compressFileList(String zipOutName, int level, List<File> paths, List<String> mlList) throws IOException, ExecutionException, InterruptedException {
        // 线程创建工厂
        final ThreadFactory factory = new ThreadFactoryBuilder().setNamePrefix("compressFileList-pool-").build();

        ExecutorService executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20), factory, new ThreadPoolExecutor.CallerRunsPolicy());
        compressFileList(zipOutName, executor, level, paths, mlList);
    }

    /**
     * 遍历压缩
     *
     * @param parallelScatterZipCreator 线程池压缩对象
     * @param inputFile                 将要压缩的文件路径,绝对路径
     * @param relativePath              相对与压缩包内的路径
     * @throws IOException
     * @throws ExecutionException
     * @throws InterruptedException
     */
    protected static void compress(ParallelScatterZipCreator parallelScatterZipCreator, File inputFile, String relativePath) throws IOException, ExecutionException, InterruptedException {
//        文件流为空，返回
        if (inputFile == null) {
            return;
        }
//        文件为文件夹，递归遍历文件
        if (inputFile.isDirectory()) {
//            获取文件内的所有文件
            File[] files = inputFile.listFiles();
            if (files == null) {
                return;
            }
//            遍历处理文件
            for (File file : files) {
                if (file.isDirectory()) {
                    compress(parallelScatterZipCreator, new File(inputFile.getAbsolutePath() + "/" + file.getName()), relativePath + "/" + file.getName());
                } else {
//                    转化为InputStreamSupplier对象
                    final InputStreamSupplier inputStreamSupplier = () -> {
                        try {
                            return new FileInputStream(file);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                            return new NullInputStream(0);
                        }
                    };
//                    添加ZipArchiveEntity对象，这里的构造函数的值，name属性，是相对于zip文件内的路径
                    ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(relativePath + "/" + file.getName());
//                    设置压缩算法
                    zipArchiveEntry.setMethod(ZipArchiveEntry.DEFLATED);
//                    设置未压缩文件的大小
                    zipArchiveEntry.setSize(file.length());
//                    添加添加ZipArchiveEntity对象到多线程压缩中
                    parallelScatterZipCreator.addArchiveEntry(zipArchiveEntry, inputStreamSupplier);
                }
            }
        } else {
//            当是文件时，直接处理
            final InputStreamSupplier inputStreamSupplier = () -> {
                try {
                    return new FileInputStream(inputFile);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                    return new NullInputStream(0);
                }
            };
//            System.out.println("1: "+relativePath);
//            System.out.println("2: "+inputFile.getName());
//            ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(relativePath + "/" + inputFile.getName());
            ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(relativePath);
            zipArchiveEntry.setMethod(ZipArchiveEntry.DEFLATED);
            zipArchiveEntry.setSize(inputFile.length());
            parallelScatterZipCreator.addArchiveEntry(zipArchiveEntry, inputStreamSupplier);
        }
    }


    public static void downloadZip(String outUrl, HttpServletResponse response){
        File file = new File(outUrl);
        if (!file.exists()) {
            return;
        }
        InputStream in = null;
        OutputStream os = null;
        try {
            String wjhz = Util.getWjHz(file.getName());
            String contentType = Util.getContenTypeByWjhz(wjhz);
            response.setContentType(contentType); // 设置返回内容格式
            response.setCharacterEncoding("UTF-8");
            in = new FileInputStream(file); //用该文件创建一个输入流
            os = response.getOutputStream(); //创建输出流
            byte[] b = new byte[1024];
            while (in.read(b) != -1) {
                os.write(b);
            }
            in.close();
            os.flush();
            os.close();
        } catch (Exception e) {
            try {
                if (null != in) in.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            try {
                if (null != os) os.close();
            } catch (IOException e2) {
                e2.printStackTrace();
            }
        }
    }
}

